/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 * File Name     : roce_cdev.c
 * Version       : v2.0
 * Created       : 2021/3/2
 * Last Modified : 2021/12/7
 * Description   : The definition of RoCE CHar device related functions.
 */

#include <asm/io.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/if_ether.h>

#include "hinic3_hw.h"
#include "hinic3_srv_nic.h"

#include "roce.h"
#include "roce_compat.h"
#include "roce_mix.h"
#include "roce_netdev.h"
#include "roce_k_ioctl.h"
#include "roce_cqm_cmd.h"
#include "roce_cdev_extension.h"
#include "roce_verbs_cmd.h"
#include "roce_cmd.h"
#include "roce_srq.h"
#include "roce_qp.h"
#include "roce_npu_cmd.h"
#include "hinic3_mt.h"

#ifdef ROCE_BONDING_EN
#include "roce_bond.h"
#endif

#ifdef ROCE_EXTEND
#include "roce_qp_extension.h"
#endif

#include "securec.h"

#define P2PCOS_MAX_VALUE 2
#define MAX_COS_NUM 0x7

static long roce3_cdev_create_ah(struct roce3_device *rdev, void *buf)
{
    long ret;
    struct rdma_ah_attr attr;
    union roce3_create_ah_buf ah_buf;
    struct ib_udata udata;

    ret = (long)copy_from_user(&ah_buf, buf, sizeof(ah_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data from user\n", __func__);
        return ret;
    }

    attr.sl = ah_buf.cmd.attr.sl;
    attr.static_rate = ah_buf.cmd.attr.static_rate;
    attr.ah_flags = (ah_buf.cmd.attr.is_global != 0) ? IB_AH_GRH : 0;
    attr.port_num = ah_buf.cmd.attr.port_num;
    attr.grh.flow_label = ah_buf.cmd.attr.grh.flow_label;
    attr.grh.sgid_index = ah_buf.cmd.attr.grh.sgid_index;
    attr.grh.hop_limit = ah_buf.cmd.attr.grh.hop_limit;
    attr.grh.traffic_class = ah_buf.cmd.attr.grh.traffic_class;
    ret = (long)memset_s(attr.roce.dmac, sizeof(attr.roce.dmac), 0, sizeof(attr.roce.dmac));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to memset dmac.\n", __func__);
        return ret;
    }
    ret = (long)memcpy_s(attr.grh.dgid.raw, ROCE_GID_LEN, ah_buf.cmd.attr.grh.dgid, ROCE_GID_LEN);
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to memcpy_s gid.\n", __func__);
        return ret;
    }

    ret = (long)memset_s(&udata, sizeof(struct ib_udata), 0, sizeof(struct ib_udata));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to memset udata.\n", __func__);
        return ret;
    }
    if (roce3_resolve_grh(rdev, &attr, &ah_buf.resp.vlan_id, &udata) != 0) {
        pr_err("[ROCE, ERR] %s: Failed to resolve grh\n", __func__);
        return -EINVAL;
    }

    ret = (long)memcpy_s(ah_buf.resp.dmac, ETH_ALEN, attr.roce.dmac, ETH_ALEN);
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to memcpy_s ah dmac.\n", __func__);
        return ret;
    }

    ret = (long)copy_to_user((void __user *)buf, &ah_buf, sizeof(ah_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data to user\n", __func__);
        return ret;
    }

    return 0;
}

#ifdef ROCE_BONDING_EN

struct roce3_get_func_table {
    __be32 func_table_val;
};

struct roce3_get_rx_port {
    __be32 rx_port;
};

struct roce3_get_udp_src_port {
    __be32 udp_src_port;
};

static struct roce3_qp *roce3_cdev_lookup_rqp(struct roce3_device *rdev, u32 qpn)
{
    cqm_object_s *cqm_obj_qp = NULL;
    struct roce3_qp *rqp = NULL;

    cqm_obj_qp = cqm_object_get(rdev->hwdev, CQM_OBJECT_SERVICE_CTX, qpn, false);
    if (cqm_obj_qp == NULL) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Can't find rqp according to qpn(0x%x), func_id(%d)\n", __func__, qpn,
            rdev->glb_func_id);
        return NULL;
    }

    rqp = cqmobj_to_roce_qp(cqm_obj_qp);
    hiudk_cqm_object_put(rdev->hwdev, cqm_obj_qp);

    return rqp;
}

static int roce3_get_qp_func_table(struct roce3_device *rdev, u32 qpn, u32 *func_table_val)
{
    int ret;
    cqm_cmd_buf_s *cqm_cmd_inbuf = NULL;
    cqm_cmd_buf_s *cqm_cmd_outbuf = NULL;
    roce_get_qp_func_table_s *get_func_tbl_inbuf = NULL;
    struct roce3_get_func_table *get_func_tbl_outbuf = NULL;

    ret = roce3_cqm_cmd_zalloc_inoutbuf(rdev->hwdev, &cqm_cmd_inbuf, (u16)sizeof(roce_get_qp_func_table_s),
        &cqm_cmd_outbuf, (u16)sizeof(roce_get_qp_func_table_s));
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to alloc cqm_cmd_inoutbuf, func_id(%d), ret(%d)\n", __func__,
            rdev->glb_func_id, ret);
        return -ENOMEM;
    }

    get_func_tbl_inbuf = (roce_get_qp_func_table_s *)cqm_cmd_inbuf->buf;
    get_func_tbl_inbuf->com.index = cpu_to_be32(qpn);
    get_func_tbl_inbuf->com.dw0.bs.cmd_bitmask = cpu_to_be16(VERBS_CMD_TYPE_QP_BITMASK);
    ret = roce3_send_qp_lb_cmd(qpn, rdev, ROCE_CMD_GET_QP_FUNC_TABLE,
        cqm_cmd_inbuf, cqm_cmd_outbuf, ROCE_CMD_TIME_CLASS_A);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to send ROCE_CMD_GET_QP_FUNC_TABLE command, func_id(%d)\n",
            __func__, rdev->glb_func_id);
        roce3_cqm_cmd_free_inoutbuf(rdev->hwdev, cqm_cmd_inbuf, cqm_cmd_outbuf);
        return -1;
    }
    get_func_tbl_outbuf = (struct roce3_get_func_table *)cqm_cmd_outbuf->buf;
    *func_table_val = be32_to_cpu(get_func_tbl_outbuf->func_table_val);

    roce3_cqm_cmd_free_inoutbuf(rdev->hwdev, cqm_cmd_inbuf, cqm_cmd_outbuf);

    return 0;
}

static long roce3_cdev_query_qp_tx_port(struct roce3_device *rdev, void *buf)
{
    long ret;
    u32 slave_cnt;
    struct roce3_qp *rqp = NULL;
    struct roce3_qp_port_buf qp_port_buf;
    u32 func_tbl_value = 0;

    ret = (long)memset_s(&qp_port_buf, sizeof(qp_port_buf), 0, sizeof(qp_port_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to memset qp_port_buf.\n", __func__);
        return ret;
    }

    ret = (long)copy_from_user(&qp_port_buf, buf, sizeof(qp_port_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data from user\n", __func__);
        return ret;
    }

    rqp = roce3_cdev_lookup_rqp(rdev, qp_port_buf.qpn);
    if (rqp == NULL) {
        pr_err("[ROCE, ERR] %s: Failed to look up rqp\n", __func__);
        return EINVAL;
    }

    if (!roce3_bond_is_active(rdev->pdev)) {
        qp_port_buf.port = 0;

        ret = (long)copy_to_user((void __user *)buf, &qp_port_buf, sizeof(qp_port_buf));
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to copy data to user\n", __func__);
            return ret;
        }

        return 0;
    }

    ret = (long)roce3_get_qp_func_table(rdev, qp_port_buf.qpn, &func_tbl_value);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to get func_tbl, func_id(%d)\n", __func__, rdev->glb_func_id);
        return ret;
    }

    slave_cnt = func_tbl_value & 0xf;
    if (slave_cnt == 0) {
        pr_err("[ROCE, ERR] %s: slave_cnt is 0\n", __func__);
        return EINVAL;
    }

    qp_port_buf.port = (func_tbl_value >>
        (ROCE_BOND_FWD_ID_TBL_ALL_BITS - ((rqp->tx_hash_value % slave_cnt + 1) * ROCE_BOND_FWD_ID_TBL_PER_BITS))) &
        ROCE_BOND_FWD_ID_TBL_PER_BITS_MASK;

    ret = (long)copy_to_user((void __user *)buf, &qp_port_buf, sizeof(qp_port_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data to user\n", __func__);
        return ret;
    }

    return 0;
}

static int roce3_modify_bond_hash_value(struct roce3_device *rdev, u32 qpn, u32 new_hash_value)
{
    int ret;
    cqm_cmd_buf_s *cqm_cmd_inbuf = NULL;
    roce_modify_hash_value_s *modify_hash_value = NULL;

    ret = roce3_cqm_cmd_zalloc_inoutbuf(rdev->hwdev, &cqm_cmd_inbuf, (u16)sizeof(roce_modify_hash_value_s), NULL,
        0);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to alloc cqm_cmd_inoutbuf, func_id(%d), ret(%d)\n", __func__,
            rdev->glb_func_id, ret);
        return -ENOMEM;
    }

    modify_hash_value = (roce_modify_hash_value_s *)cqm_cmd_inbuf->buf;

    modify_hash_value->com.index = cpu_to_be32(qpn);
    modify_hash_value->com.dw0.bs.cmd_bitmask = cpu_to_be16(VERBS_CMD_TYPE_QP_BITMASK);
    modify_hash_value->hash_value = cpu_to_be32(new_hash_value);

    ret = roce3_send_qp_lb_cmd(qpn, rdev, ROCE_CMD_MODIFY_HASH_VALUE_QP, cqm_cmd_inbuf, NULL, ROCE_CMD_TIME_CLASS_A);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to send MODIFY_HASH_VALUE_QP command, func_id(%d)\n", __func__,
            rdev->glb_func_id);
        roce3_cqm_cmd_free_inoutbuf(rdev->hwdev, cqm_cmd_inbuf, NULL);
        return -1;
    }

    roce3_cqm_cmd_free_inoutbuf(rdev->hwdev, cqm_cmd_inbuf, NULL);

    return 0;
}

static long roce3_cdev_set_qp_tx_port(struct roce3_device *rdev, const void *buf)
{
    long ret;
    u32 new_tx_hash_value;
    u32 slave_cnt;
    u32 target_hash_port;
    u32 old_hash_port;
    struct roce3_qp *rqp = NULL;
    struct roce3_qp_port_buf qp_port_buf;
    u32 func_tbl_value = 0;

    ret = (long)copy_from_user(&qp_port_buf, buf, sizeof(qp_port_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data from user\n", __func__);
        return ret;
    }

    /* check qp exist */
    rqp = roce3_cdev_lookup_rqp(rdev, qp_port_buf.qpn);
    if (rqp == NULL) {
        pr_err("[ROCE, ERR] %s: Failed to look up rqp\n", __func__);
        return -EINVAL;
    }

    if (!roce3_bond_is_active(rdev->pdev)) {
        return 0;
    }

    ret = (long)roce3_get_qp_func_table(rdev, qp_port_buf.qpn, &func_tbl_value);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to get func_tbl, func_id(%d)\n", __func__, rdev->glb_func_id);
        return ret;
    }

    slave_cnt = func_tbl_value & 0xf;
    if (slave_cnt == 0) {
        pr_err("[ROCE, ERR] %s: slave_cnt is 0\n", __func__);
        return EINVAL;
    }
    target_hash_port = qp_port_buf.port & ROCE_BOND_FWD_ID_TBL_PER_BITS_MASK;

    old_hash_port = (func_tbl_value >>
        (ROCE_BOND_FWD_ID_TBL_ALL_BITS - ((rqp->tx_hash_value % slave_cnt + 1) * ROCE_BOND_FWD_ID_TBL_PER_BITS))) &
        ROCE_BOND_FWD_ID_TBL_PER_BITS_MASK;

    if (target_hash_port == old_hash_port) {
        return 0;
    }

    new_tx_hash_value = rqp->tx_hash_value + (((slave_cnt - old_hash_port) + target_hash_port) % slave_cnt);

    ret = (long)roce3_modify_bond_hash_value(rdev, rqp->qpn, new_tx_hash_value);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to modify QP(0x%06x) hash value, func_id(%d)\n", __func__,
            rqp->qpn, rdev->glb_func_id);
        return EIO;
    }

    rqp->tx_hash_value = new_tx_hash_value;

    return 0;
}

static int roce3_get_qp_rx_port(struct roce3_device *rdev, u32 qpn, u32 *rx_port)
{
    int ret;
    cqm_cmd_buf_s *cqm_cmd_inbuf = NULL;
    cqm_cmd_buf_s *cqm_cmd_outbuf = NULL;
    roce_get_qp_rx_port_s *get_rx_port_inbuf = NULL;
    struct roce3_get_rx_port *get_rx_port_outbuf = NULL;

    ret = roce3_cqm_cmd_zalloc_inoutbuf(rdev->hwdev, &cqm_cmd_inbuf, (u16)sizeof(roce_get_qp_rx_port_s),
        &cqm_cmd_outbuf, (u16)sizeof(roce_get_qp_rx_port_s));
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to alloc cqm_cmd_inoutbuf, func_id(%d), ret(%d)\n", __func__,
            rdev->glb_func_id, ret);
        return -ENOMEM;
    }

    get_rx_port_inbuf = (roce_get_qp_rx_port_s *)cqm_cmd_inbuf->buf;
    get_rx_port_inbuf->com.index = cpu_to_be32(qpn);
    get_rx_port_inbuf->com.dw0.bs.cmd_bitmask = cpu_to_be16(VERBS_CMD_TYPE_QP_BITMASK);
    ret = roce3_send_qp_lb_cmd(qpn, rdev, ROCE_CMD_GET_QP_RX_PORT,
        cqm_cmd_inbuf, cqm_cmd_outbuf, ROCE_CMD_TIME_CLASS_A);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to send ROCE_CMD_GET_QP_RX_PORT command, func_id(%d)\n",
            __func__, rdev->glb_func_id);
        roce3_cqm_cmd_free_inoutbuf(rdev->hwdev, cqm_cmd_inbuf, cqm_cmd_outbuf);
        return -1;
    }
    get_rx_port_outbuf = (struct roce3_get_rx_port *)cqm_cmd_outbuf->buf;
    *rx_port = be32_to_cpu(get_rx_port_outbuf->rx_port);

    roce3_cqm_cmd_free_inoutbuf(rdev->hwdev, cqm_cmd_inbuf, cqm_cmd_outbuf);

    return 0;
}

static long roce3_cdev_query_qp_rx_port(struct roce3_device *rdev, void *buf)
{
    long ret;
    u32 rx_port = 0;
    struct roce3_qp *rqp = NULL;
    struct roce3_qp_port_buf qp_port_buf;

    ret = (long)memset_s(&qp_port_buf, sizeof(qp_port_buf), 0, sizeof(qp_port_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to memset qp_port_buf.\n", __func__);
        return ret;
    }
    ret = (long)copy_from_user(&qp_port_buf, buf, sizeof(qp_port_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data from user\n", __func__);
        return ret;
    }

    rqp = roce3_cdev_lookup_rqp(rdev, qp_port_buf.qpn);
    if (rqp == NULL) {
        pr_err("[ROCE, ERR] %s: Failed to look up rqp\n", __func__);
        return EINVAL;
    }

    if (rqp->qp_type != IB_QPT_RC) {
        pr_err("[ROCE, ERR] %s: not support qp type(%d), only support RC.\n", __func__, rqp->qp_type);
        return EINVAL;
    }

    if (!roce3_bond_is_active(rdev->pdev)) {
        qp_port_buf.port = 0;

        ret = (long)copy_to_user((void __user *)buf, &qp_port_buf, sizeof(qp_port_buf));
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to copy data to user\n", __func__);
            return ret;
        }

        return 0;
    }

    ret = (long)roce3_get_qp_rx_port(rdev, qp_port_buf.qpn, &rx_port);
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to get QP(0x%06x) rx port\n", __func__, qp_port_buf.qpn);
        return ret;
    }

    qp_port_buf.port = rx_port;

    ret = (long)copy_to_user((void __user *)buf, &qp_port_buf, sizeof(qp_port_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data to user\n", __func__);
        return ret;
    }

    return 0;
}

static long roce3_cdev_query_next_wqe_idx(struct roce3_device *rdev, void *buf)
{
    long ret;
    int ret_adm;
    struct roce3_dfx_query_inbuf dfx_query_inbuf = {0};
    u32 out_size = (u32)sizeof(union roce3_dfx_query_outbuf);
    union roce3_dfx_query_outbuf dfx_query_outbuf;
    int next_wqe_idx;

    ret = (long)copy_from_user(&dfx_query_inbuf, buf, sizeof(dfx_query_inbuf));
    if (ret != 0) {
        (void)pr_err("[ROCE] %s: Failed to copy data from user, ret(0x%lux)\n", __func__, ret);
        return ret;
    }

    dfx_query_inbuf.cmd_type = ROCE_CMD_GET_SRQC_FROM_CACHE;
    ret_adm = roce3_adm_dfx_query(rdev, &dfx_query_inbuf, sizeof(struct roce3_dfx_query_inbuf),
        &dfx_query_outbuf, &out_size);
    if (ret_adm != 0) {
        (void)pr_err("[ROCE] %s: Failed to do roce_adm_dfx_query, ret(%d)\n", __func__, ret_adm);
        return EINVAL;
    }

    dfx_query_outbuf.srq_ctx.dw2.value = be32_to_cpu(dfx_query_outbuf.srq_ctx.dw2.value);
    dfx_query_outbuf.srq_ctx.dw3.value = be32_to_cpu(dfx_query_outbuf.srq_ctx.dw3.value);
    dfx_query_outbuf.srq_ctx.dw5.value = be32_to_cpu(dfx_query_outbuf.srq_ctx.dw5.value);

    if (dfx_query_outbuf.srq_ctx.dw2.bs.container_en == 0) {
        next_wqe_idx = (int)(dfx_query_outbuf.srq_ctx.dw5.bs.next_wqe_idx);
    } else {
        u32 container_mode = MAX_SUPPORT_CONTAINER_MODE - dfx_query_outbuf.srq_ctx.dw2.bs_c.container_size;
        u32 container_size = roce3_get_container_sz(container_mode);
        next_wqe_idx = (int)(container_size * dfx_query_outbuf.srq_ctx.dw3.bs_c.head_index);
    }
    ret = (long)copy_to_user((void __user *)buf, &next_wqe_idx, sizeof(int));
    if (ret != 0) {
        (void)pr_err("[ROCE] %s: Failed to copy next_wqe_idx to user, ret(0x%lx)\n", __func__, ret);
        return ret;
    }

    return 0;
}

static void roce3_cdev_set_bond_port_info(
    struct roce3_bond_device *bond_dev, u32 func_tbl_value, struct roce3_bond_port_info_buf *bond_port_info_buf)
{
    s8 i, j;

    bond_port_info_buf->original_port_num = bond_dev->slave_cnt;
    for (i = 0, j = 0; i < ROCE_BOND_MAX_PORTS; i++) {
        if (((bond_dev->func_use_bits >> i) & 1U) != 0) {
            bond_port_info_buf->original_port[j] = i;
            j++;

            if (j >= bond_dev->slave_cnt) {
                break;
            }
        }
    }

    bond_port_info_buf->alive_port_num = func_tbl_value & 0xf;
    for (i = 0; i < bond_port_info_buf->alive_port_num; i++) {
        bond_port_info_buf->alive_port[i] =
            (func_tbl_value >> (ROCE_BOND_FWD_ID_TBL_ALL_BITS - ((i + 1) * ROCE_BOND_FWD_ID_TBL_PER_BITS))) &
            ROCE_BOND_FWD_ID_TBL_PER_BITS_MASK;
    }
}

static long roce3_cdev_query_bond_port_info(struct roce3_device *rdev, void *buf)
{
    long ret;
    struct roce3_bond_device *bond_dev = NULL;
    struct roce3_bond_port_info_buf bond_port_info_buf;
    u32 func_tbl_value = 0;

    ret = (long)memset_s(&bond_port_info_buf, sizeof(bond_port_info_buf), 0, sizeof(bond_port_info_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to memset bond_port_info_buf.\n", __func__);
        return ret;
    }

    if (!roce3_bond_is_active(rdev->pdev)) {
        ret = (long)copy_to_user((void __user *)buf, &bond_port_info_buf, sizeof(bond_port_info_buf));
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to copy data to user\n", __func__);
            return ret;
        }

        return 0;
    }

    bond_dev = roce3_bond_dev_get(rdev->pdev, false);
    if (bond_dev == NULL) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Can't find bond_dev, func_id(%d)\n", __func__, rdev->glb_func_id);
        return EINVAL;
    }

    ret = roce3_get_func_table(rdev->hwdev, rdev->glb_func_id, &func_tbl_value);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to get func_tbl, func_id(%d)\n", __func__,
            rdev->glb_func_id);
        return ret;
    }

    roce3_cdev_set_bond_port_info(bond_dev, func_tbl_value, &bond_port_info_buf);

    ret = (long)copy_to_user((void __user *)buf, &bond_port_info_buf, sizeof(bond_port_info_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data to user\n", __func__);
        return ret;
    }

    return 0;
}

static int roce3_modify_udp_src_port(struct roce3_device *rdev, u32 qpn, u32 new_udp_src_port)
{
    int ret;
    cqm_cmd_buf_s *cqm_cmd_inbuf = NULL;
    roce_modify_udp_src_port_s *modify_udp_src_port = NULL;

    ret = roce3_cqm_cmd_zalloc_inoutbuf(rdev->hwdev, &cqm_cmd_inbuf, (u16)sizeof(roce_modify_udp_src_port_s),
        NULL, 0);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to alloc cqm_cmd_inoutbuf, func_id(%d), ret(%d)\n", __func__,
            rdev->glb_func_id, ret);
        return -ENOMEM;
    }

    modify_udp_src_port = (roce_modify_udp_src_port_s *)cqm_cmd_inbuf->buf;
    modify_udp_src_port->com.index = cpu_to_be32(qpn);
    modify_udp_src_port->com.dw0.bs.cmd_bitmask = cpu_to_be16(VERBS_CMD_TYPE_QP_BITMASK);
    modify_udp_src_port->udp_src_port = cpu_to_be32(new_udp_src_port);

    ret = roce3_send_qp_lb_cmd(qpn, rdev, ROCE_CMD_MODIFY_UDP_SRC_PORT_QP, cqm_cmd_inbuf, NULL, ROCE_CMD_TIME_CLASS_A);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to send MODIFY_HASH_VALUE_QP command, func_id(%d)\n", __func__,
            rdev->glb_func_id);
        roce3_cqm_cmd_free_inoutbuf(rdev->hwdev, cqm_cmd_inbuf, NULL);
        return -1;
    }

    roce3_cqm_cmd_free_inoutbuf(rdev->hwdev, cqm_cmd_inbuf, NULL);

    return 0;
}

static long roce3_cdev_set_qp_udp_src_port(struct roce3_device *rdev, const void *buf)
{
    long ret;
    struct roce3_qp *rqp = NULL;
    struct roce3_qp_udp_src_port_buf qp_udp_src_port_buf;

    ret = (long)copy_from_user(&qp_udp_src_port_buf, buf, sizeof(qp_udp_src_port_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data from user\n", __func__);
        return ret;
    }

    /* check qp exist */
    rqp = roce3_cdev_lookup_rqp(rdev, qp_udp_src_port_buf.qpn);
    if (rqp == NULL) {
        pr_err("[ROCE, ERR] %s: Failed to look up rqp\n", __func__);
        return EINVAL;
    }

    ret = (long)roce3_modify_udp_src_port(rdev, rqp->qpn, qp_udp_src_port_buf.udp_src_port);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to modify QP(0x%06x) hash value, func_id(%d)\n", __func__,
            rqp->qpn, rdev->glb_func_id);
        return EIO;
    }

    return 0;
}

static int roce3_get_qp_udp_src_port(struct roce3_device *rdev, u32 qpn, u32 *udp_src_port)
{
    int ret;
    cqm_cmd_buf_s *cqm_cmd_inbuf = NULL;
    cqm_cmd_buf_s *cqm_cmd_outbuf = NULL;
    roce_get_qp_udp_src_port_s *get_udp_src_port = NULL;
    struct roce3_get_udp_src_port *get_udp_src_port_outbuf = NULL;

    ret = roce3_cqm_cmd_zalloc_inoutbuf(rdev->hwdev, &cqm_cmd_inbuf, (u16)sizeof(roce_get_qp_udp_src_port_s),
        &cqm_cmd_outbuf, (u16)sizeof(roce_get_qp_udp_src_port_s));
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to alloc cqm_cmd_inoutbuf, func_id(%d), ret(%d)\n", __func__,
            rdev->glb_func_id, ret);
        return -ENOMEM;
    }

    get_udp_src_port = (roce_get_qp_udp_src_port_s *)cqm_cmd_inbuf->buf;
    get_udp_src_port->com.index = cpu_to_be32(qpn);
    get_udp_src_port->com.dw0.bs.cmd_bitmask = cpu_to_be16(VERBS_CMD_TYPE_QP_BITMASK);

    ret = roce3_send_qp_lb_cmd(qpn, rdev, ROCE_CMD_GET_UDP_SRC_PORT_QP,
        cqm_cmd_inbuf, cqm_cmd_outbuf, ROCE_CMD_TIME_CLASS_A);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Failed to send ROCE_CMD_GET_QP_RX_PORT command, func_id(%d)\n",
            __func__, rdev->glb_func_id);
        roce3_cqm_cmd_free_inoutbuf(rdev->hwdev, cqm_cmd_inbuf, cqm_cmd_outbuf);
        return -1;
    }

    get_udp_src_port_outbuf = (struct roce3_get_udp_src_port *)cqm_cmd_outbuf->buf;
    *udp_src_port = be32_to_cpu(get_udp_src_port_outbuf->udp_src_port);

    roce3_cqm_cmd_free_inoutbuf(rdev->hwdev, cqm_cmd_inbuf, cqm_cmd_outbuf);

    return 0;
}

static long roce3_cdev_query_qp_udp_src_port(struct roce3_device *rdev, void *buf)
{
    long ret;
    u32 udp_src_port = 0;
    struct roce3_qp_udp_src_port_buf qp_udp_src_port_buf;

    ret = (long)memset_s(&qp_udp_src_port_buf, sizeof(qp_udp_src_port_buf), 0, sizeof(qp_udp_src_port_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to memset qp_udp_src_port_buf.\n", __func__);
        return ENOMEM;
    }
    ret = (long)copy_from_user(&qp_udp_src_port_buf, buf, sizeof(qp_udp_src_port_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data from user\n", __func__);
        return ret;
    }

    ret = (long)roce3_get_qp_udp_src_port(rdev, qp_udp_src_port_buf.qpn, &udp_src_port);
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to get QP(0x%06x) udp src port\n", __func__, qp_udp_src_port_buf.qpn);
        return ret;
    }

    qp_udp_src_port_buf.udp_src_port = udp_src_port;

    ret = (long)copy_to_user((void __user *)buf, &qp_udp_src_port_buf, sizeof(qp_udp_src_port_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data to user\n", __func__);
        return ret;
    }

    return 0;
}

#endif

static int roce3_cdev_open(struct inode *inode, struct file *filp)
{
    struct roce3_cdev *dev = NULL;
    struct roce3_cdev_file *file = NULL;

    file = kzalloc(sizeof(*file), GFP_KERNEL);
    if (file == NULL) {
        pr_err("[ROCE, ERR] %s: Failed to open cdev\n", __func__);
        return -ENOMEM;
    }

    dev = container_of(inode->i_cdev, struct roce3_cdev, cdev);
    file->cdev = dev;
    filp->private_data = file;

    return 0;
}

static int roce3_cdev_close(struct inode *inode, struct file *filp)
{
    struct roce3_cdev_file *file = filp->private_data;

    kfree(file);

    return 0;
}

static ssize_t roce3_cdev_read(struct file *fp, char __user *buf, size_t size, loff_t *pos)
{
    (void)(fp);
    (void)(buf);
    (void)(size);
    (void)(pos);
    return -EOPNOTSUPP;
}

static ssize_t roce3_cdev_write(struct file *fp, const char __user *buf, size_t size, loff_t *pos)
{
    (void)(fp);
    (void)(buf);
    (void)(size);
    (void)(pos);
    return -EOPNOTSUPP;
}

#ifdef ROCE_BONDING_EN
static long roce3_cdev_bonding_ioctl_part_1(unsigned int cmd, unsigned long arg, struct roce3_device *rdev)
{
    long ret = 0;

    switch (cmd) {
        case ROCE_CMD_QUERY_QP_TX_PORT:
            ret = roce3_cdev_query_qp_tx_port(rdev, (void *)(uintptr_t)arg);
            if (ret != 0) {
                pr_err("[ROCE, ERR] %s: roce3_cdev_query_qp_tx_port failed.\n", __func__);
                return ret;
            }
            break;

        case ROCE_CMD_SET_QP_TX_PORT:
            ret = roce3_cdev_set_qp_tx_port(rdev, (void *)(uintptr_t)arg);
            if (ret != 0) {
                pr_err("[ROCE, ERR] %s: roce3_cdev_set_qp_tx_port failed.\n", __func__);
                return ret;
            }
            break;
        case ROCE_CMD_QUERY_QP_RX_PORT:
            ret = roce3_cdev_query_qp_rx_port(rdev, (void *)(uintptr_t)arg);
            if (ret != 0) {
                pr_err("[ROCE, ERR] %s: roce3_cdev_query_qp_rx_port failed.\n", __func__);
                return ret;
            }
            break;
        default:
            pr_err("[ROCE, ERR] %s: Not supported cmd(%d).\n", __func__, cmd);
            ret = -1;
            return ret;
    }

    return ret;
}

static long roce3_cdev_bonding_ioctl_part_2(unsigned int cmd, unsigned long arg, struct roce3_device *rdev)
{
    long ret = 0;

    switch (cmd) {
        case ROCE_CMD_QUERY_BOND_PORT_INFO:
            ret = roce3_cdev_query_bond_port_info(rdev, (void *)(uintptr_t)arg);
            if (ret != 0) {
                pr_err("[ROCE, ERR] %s: roce3_cdev_query_qp_tx_port failed.\n", __func__);
                return ret;
            }
            break;

        case ROCE_CMD_SET_QP_UDP_SRC_PORT:
            ret = roce3_cdev_set_qp_udp_src_port(rdev, (void *)(uintptr_t)arg);
            if (ret != 0) {
                pr_err("[ROCE, ERR] %s: roce3_cdev_set_qp_tx_port failed.\n", __func__);
                return ret;
            }
            break;
        case ROCE_CMD_QUERY_QP_UDP_SRC_PORT:
            ret = roce3_cdev_query_qp_udp_src_port(rdev, (void *)(uintptr_t)arg);
            if (ret != 0) {
                pr_err("[ROCE, ERR] %s: roce3_cdev_query_qp_rx_port failed.\n", __func__);
                return ret;
            }
            break;

        case ROCE_CMD_QUERY_NEXT_WQE_IDX:
            ret = roce3_cdev_query_next_wqe_idx(rdev, (void *)(uintptr_t)arg);
            if (ret != 0) {
                pr_err("[ROCE, ERR] %s: roce3_cdev_query_next_wqe_idx failed.\n", __func__);
                return ret;
            }
            break;

        default:
            pr_err("[ROCE, ERR] %s: Not supported cmd(%d).\n", __func__, cmd);
            ret = -1;
            return ret;
    }

    return ret;
}
static long roce3_cdev_ioctl_bonding(unsigned int cmd, unsigned long arg, struct roce3_device *rdev)
{
    long ret = 0;

    if (cmd < ROCE_CMD_QUERY_BOND_PORT_INFO) {
        ret = roce3_cdev_bonding_ioctl_part_1(cmd, arg, rdev);
        if (ret != 0) {
            return ret;
        }
    } else {
        ret = roce3_cdev_bonding_ioctl_part_2(cmd, arg, rdev);
        if (ret != 0) {
            return ret;
        }
    }

    return ret;
}

static int roce3_bond_get_dcb_info_from_buddy(struct roce3_device *rdev, struct roce3_bond_device *bond_dev)
{
    int i;
    int ret;
    u16 func_id;
    void *hwdev = NULL;
    struct hinic3_dcb_state dcb_info, buddy_dcb_info;

    roce3_bond_for_each_func(func_id, 0, bond_dev)
    {
        if (func_id == rdev->glb_func_id) {
            continue;
        }

        if ((bond_dev->active_slave_mask & (1U << func_id)) == 0) {
            continue;
        }

        (void)memset_s(&buddy_dcb_info, sizeof(buddy_dcb_info), 0, sizeof(buddy_dcb_info));

        hwdev = bond_dev->bond_func[func_id].hwdev;
        ret = hinic3_get_dcb_state(hwdev, &buddy_dcb_info);
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to get dcb state, ret(%d)\n", __func__, ret);
            return ret;
        }

        (void)memset_s(&dcb_info, sizeof(dcb_info), 0, sizeof(dcb_info));

        ret = hinic3_get_dcb_state(rdev->hwdev, &dcb_info);
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to get dcb state, ret(%d)\n", __func__, ret);
            return ret;
        }

        buddy_dcb_info.default_cos = dcb_info.default_cos;
        for (i = 0; i < NIC_DCB_UP_MAX; i++) {
            if (buddy_dcb_info.pcp2cos[i] > P2PCOS_MAX_VALUE) {
                buddy_dcb_info.pcp2cos[i] = dcb_info.default_cos;
            }
        }

        ret = memcpy_s(&rdev->dcb_info, sizeof(rdev->dcb_info), &buddy_dcb_info, sizeof(buddy_dcb_info));
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to do memcpy_s.\n", __func__);
            return -EINVAL;
        }

        return 0;
    }

    return 0;
}

int roce3_bond_get_dcb_info(struct roce3_device *rdev)
{
    struct roce3_bond_device *bond_dev = NULL;
    bool bond_dev_up;
    int ret;

    if (roce3_bond_is_active(rdev->pdev)) {
        bond_dev = roce3_bond_dev_get(rdev->pdev, false);
        if (bond_dev == NULL) {
            pr_err("[ROCE, ERR] %s: Failed to get bond_dev\n", __func__);
            return -1;
        }

        bond_dev_up = (bond_dev->active_slave_mask & ((unsigned int)1 << rdev->glb_func_id)) != 0;
        if (!bond_dev_up) {
            ret = hinic3_get_dcb_state(rdev->hwdev, &rdev->dcb_info);
            if (ret != 0) {
                pr_err("[ROCE, ERR] %s: Failed to get dcb state, ret(%d)\n", __func__, ret);
                return ret;
            }
        } else {
            ret = roce3_bond_get_dcb_info_from_buddy(rdev, bond_dev);
            if (ret != 0) {
                pr_err("[ROCE, ERR] %s: Failed to get dcb state from buddy, ret(%d)\n", __func__, ret);
                return ret;
            }
        }
    }

    return 0;
}
#endif

static long roce3_cdev_query_dcb(struct roce3_device *rdev, void *buf)
{
    union roce3_query_dcb_buf dcb_buf;
    long ret;
    int get_group_id;
    struct roce_group_id group_id = {0};
    u8 cos;
    ret = (long)copy_from_user(&dcb_buf, buf, sizeof(dcb_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data from user\n", __func__);
        return ret;
    }

#ifdef ROCE_BONDING_EN
    if (roce3_bond_get_dcb_info(rdev) != 0) {
        pr_err("[ROCE, ERR] %s: Failed to get dcb info, ret(%lu)\n", __func__, ret);
        return (-EINVAL);
    }
#endif

    ret = (long)roce3_get_dcb_cfg_cos(rdev, (roce3_get_cos_inbuf_s *)(void *)(&dcb_buf), &cos);
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to get cos from dcb info, ret:%ld\n", __func__, ret);
        return ret;
    }

    dcb_buf.resp.cos = cos;

    if (rdev->is_vroce) {
        get_group_id = roce3_get_group_id(rdev->glb_func_id, rdev->hwdev, &group_id);
        if (get_group_id != 0) {
            pr_warn("Failed to get group id, ret(%d)", get_group_id);
        } else {
            rdev->group_rc_cos = group_id.group_rc_cos;
            rdev->group_ud_cos = group_id.group_ud_cos;
            rdev->group_xrc_cos = group_id.group_xrc_cos;
        }
        if (dcb_buf.cmd.dscp_type == (u8)IB_QPT_RC) {
            dcb_buf.resp.cos = rdev->group_rc_cos & MAX_COS_NUM;
        } else if (dcb_buf.cmd.dscp_type == (u8)IB_QPT_UD) {
            dcb_buf.resp.cos = rdev->group_ud_cos & MAX_COS_NUM;
        } else {
            dcb_buf.resp.cos = rdev->group_xrc_cos & MAX_COS_NUM;
        }
    }

    ret = (long)copy_to_user((void __user *)buf, &dcb_buf, sizeof(dcb_buf));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to copy data to user\n", __func__);
        return ret;
    }
    return 0;
}

static long roce3_cdev_ioctl_non_bonding(unsigned int cmd, struct roce3_device *rdev, unsigned long arg)
{
    long ret = 0;

    switch (cmd) {
        case ROCE_CMD_QUERY_DCB:
            ret = roce3_cdev_query_dcb(rdev, (void *)(uintptr_t)arg);
            if (ret != 0) {
                pr_err("[ROCE, ERR] %s: roce3_cdev_query_dcb failed.\n", __func__);
                return ret;
            }
            break;

        case ROCE_CMD_CREATE_AH:
            ret = roce3_cdev_create_ah(rdev, (void *)(uintptr_t)arg);
            if (ret != 0) {
                pr_err("[ROCE, ERR] %s: roce3_cdev_create_ah failed.\n", __func__);
                return ret;
            }
            break;

        default:
            ret = ioctl_non_bonding_extend(cmd, rdev, arg);
            if (ret != NOT_SUPOORT_TYPE) {
                return ret;
            }
            pr_err("[ROCE, ERR] %s: Not supported cmd(%d).\n", __func__, cmd);
            ret = -1;
            return ret;
    }

    return ret;
}

static long roce3_ioctrl_version(struct roce3_device *rdev, void *buf);
static long roce3_ioctrl_reserved_1(struct roce3_device *rdev, void *buf);
static long roce3_ioctrl_reserved_2(struct roce3_device *rdev, void *buf);
static long roce3_ioctrl_reserved_3(struct roce3_device *rdev, void *buf);
static long roce3_ioctrl_reserved_4(struct roce3_device *rdev, void *buf);
static long roce3_ioctrl_reserved_5(struct roce3_device *rdev, void *buf);
static long roce3_ioctrl_reserved_6(struct roce3_device *rdev, void *buf);

typedef struct tag_HW_ROCE_IOCTL_ACTION {
    unsigned int cmd;
    long (*action_func)(struct roce3_device *rdev, void *buf);
}HW_ROCE_IOCTL_ACTION;

static HW_ROCE_IOCTL_ACTION ioctl_reserved_tbl[] = {
    {HW_ROCE_CMD_VERSION,     roce3_ioctrl_version},
    {HW_ROCE_CMD_RESERVED_1,  roce3_ioctrl_reserved_1},
    {HW_ROCE_CMD_RESERVED_2,  roce3_ioctrl_reserved_2},
    {HW_ROCE_CMD_RESERVED_3,  roce3_ioctrl_reserved_3},
    {HW_ROCE_CMD_RESERVED_4,  roce3_ioctrl_reserved_4},
    {HW_ROCE_CMD_RESERVED_5,  roce3_ioctrl_reserved_5},
    {HW_ROCE_CMD_RESERVED_6,  roce3_ioctrl_reserved_6},
};

static long roce3_cdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    long ret = 0;
    struct roce3_device *rdev = NULL;
    struct roce3_cdev_file *cdev_file = NULL;
    unsigned int idx = 0;

    if (_IOC_TYPE(cmd) != ROCE_IOCTL_MAGIC) {
        pr_err("[ROCE, ERR] %s: ROCE_IOCTL_MAGIC check failed.\n", __func__);
        return -EINVAL;
    }

    cdev_file = filp->private_data;
    rdev = container_of(cdev_file->cdev, struct roce3_device, cdev);

    if (cmd < ROCE_CMD_QUERY_QP_TX_PORT) {
        ret = roce3_cdev_ioctl_non_bonding(cmd, rdev, arg);
        return ret;
    }
#ifdef ROCE_BONDING_EN
    else if (cmd < ROCE_CMD_MAX) {
        ret = roce3_cdev_ioctl_bonding(cmd, arg, rdev);
        return ret;
    }
#endif
#ifdef ROCE_EXTEND
    else if (cmd == HW_ROCE_EXT_CMD_SET_QP_ATTR) {
        ret = (long)roce3_set_qp_ext_attr(rdev, (void *)(uintptr_t)arg);
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: roce3_set_qp_ext_attr failed.\n", __func__);
        }
        return ret;
    } else if (cmd == HW_ROCE_EXT_CMD_CREATE_SQPC) {
        ret = (long)roce3_vbs_create_sqpc(rdev, (void *)(uintptr_t)arg);
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: roce3_vbs_create_sqpc failed.\n", __func__);
        }
        return ret;
    }
#endif

    for (idx = 0; idx < sizeof(ioctl_reserved_tbl) / sizeof((ioctl_reserved_tbl)[0]); idx++) {
        if (cmd == ioctl_reserved_tbl[idx].cmd && ioctl_reserved_tbl[idx].action_func != NULL) {
            ret = ioctl_reserved_tbl[idx].action_func(rdev, (void *)(uintptr_t)arg);
            if (ret != 0) {
                pr_err("[ROCE, ERR] %s: cmd 0x%x ioctl action failed ret %ld.\n", __func__, cmd, ret);
            }
            return ret;    // cmd excute end
        }
    }
    // unsupport cmd
    pr_err("[ROCE, ERR] %s: Not supported cmd(%d).\n", __func__, cmd);
    return -EOPNOTSUPP;
}

static const struct file_operations roce3_cdev_fops = {
    .owner = THIS_MODULE,
    .open = roce3_cdev_open,
    .release = roce3_cdev_close,
    .read = roce3_cdev_read,
    .write = roce3_cdev_write,
    .unlocked_ioctl = roce3_cdev_ioctl,
};

#define ROCE_BASE_CDEV_MAJOR 232
#define ROCE_BASE_CDEV_MINOR 256
#define ROCE_MAX_DEVICES 256

/*lint -e708*/
static DEFINE_SPINLOCK(map_lock);
/*lint +e708*/
static DECLARE_BITMAP(dev_map, ROCE_MAX_DEVICES);

static char *roce3_devnode(struct device *dev, umode_t *mode)
{
    if (mode) {
        *mode = DEFAULT_ROCE_DEV_NODE_PRI;
    }

    return kasprintf(GFP_KERNEL, "%s", dev_name(dev));
}

static int roce3_init_cdev_info(struct roce3_device *rdev)
{
    int ret;

    cdev_init(&rdev->cdev.cdev, &roce3_cdev_fops);
    rdev->cdev.cdev.owner = THIS_MODULE;
    rdev->cdev.cdev.ops = &roce3_cdev_fops;

    ret = cdev_add(&rdev->cdev.cdev, rdev->cdev.dev_major, 1);
    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Add cdev failed, func_id(%d)\n", __func__, rdev->glb_func_id);
        goto err_add_cdev;
    }

    /*lint -e160*/
    rdev->cdev.cdev_class = class_create(THIS_MODULE, rdev->ib_dev.name);
    /*lint +e160*/
    if (IS_ERR(rdev->cdev.cdev_class)) {
        ret = (int)PTR_ERR(rdev->cdev.cdev_class);
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Create class failed, func_id(%d)\n", __func__, rdev->glb_func_id);
        goto err_create_class;
    }

    /*lint -e10 -e40 -e63*/
    rdev->cdev.cdev_class->devnode = roce3_devnode;
    /*lint +e10 +e40 +e63*/

    rdev->cdev.dev = device_create(rdev->cdev.cdev_class, NULL, rdev->cdev.dev_major, NULL, "%s", rdev->ib_dev.name);
    if (IS_ERR(rdev->cdev.dev)) {
        ret = (int)PTR_ERR(rdev->cdev.dev);
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Create device failed, func_id(%d)\n", __func__, rdev->glb_func_id);
        goto err_create_device;
    }

    return 0;

err_create_device:
    class_destroy(rdev->cdev.cdev_class);

err_create_class:
    cdev_del(&rdev->cdev.cdev);

err_add_cdev:
    return ret;
}

int roce3_init_cdev(struct roce3_device *rdev)
{
    int ret;

    spin_lock(&map_lock);
    rdev->cdev.dev_num = (int)find_first_zero_bit(dev_map, ROCE_MAX_DEVICES);
    if (rdev->cdev.dev_num >= ROCE_MAX_DEVICES) {
        spin_unlock(&map_lock);
        return -ENOMEM;
    }

    rdev->cdev.dev_major = MKDEV(ROCE_BASE_CDEV_MAJOR, (u32)rdev->cdev.dev_num + ROCE_BASE_CDEV_MINOR);
    set_bit((u32)rdev->cdev.dev_num, dev_map);
    spin_unlock(&map_lock);

    ret = register_chrdev_region(rdev->cdev.dev_major, 1, rdev->ib_dev.name);
    if (ret == -EBUSY) {
        /* alloc dynamic cdev by OS */
        ret = alloc_chrdev_region(&(rdev->cdev.dev_major), ROCE_BASE_CDEV_MINOR, 1, rdev->ib_dev.name);
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: alloc cdev region, major(%d), minor(%d), func_id(%d), ret(%d)\n",
            __func__, MAJOR(rdev->cdev.dev_major), MINOR(rdev->cdev.dev_major), rdev->glb_func_id, ret);
    }

    if (ret != 0) {
        dev_err(rdev->hwdev_hdl, "[ROCE, ERR] %s: Register region failed, func_id(%d), ret(%d)\n", __func__,
            rdev->glb_func_id, ret);
        goto err_register_region;
    }

    ret = roce3_init_cdev_info(rdev);
    if (ret != 0) {
        goto err_init_cdev_info;
    }

    return 0;

err_init_cdev_info:
    unregister_chrdev_region(rdev->cdev.dev_major, 1);

err_register_region:
    clear_bit(rdev->cdev.dev_num, dev_map);

    return ret;
}

void roce3_remove_cdev(struct roce3_device *rdev)
{
    device_destroy(rdev->cdev.cdev_class, rdev->cdev.dev_major);

    class_destroy(rdev->cdev.cdev_class);

    cdev_del(&rdev->cdev.cdev);

    unregister_chrdev_region(rdev->cdev.dev_major, 1);

    clear_bit(rdev->cdev.dev_num, dev_map);
}

// get version
static long roce3_ioctrl_version(struct roce3_device *rdev, void *buf)
{
    char ver[VERSION_LEN] = "1.01";
    long ret = copy_to_user(buf, ver, VERSION_LEN);
    return ret;
}

static long roce3_ioctrl_reserved_1(struct roce3_device *rdev, void *buf)
{
    (void)(rdev);
    (void)(buf);
    return NOT_SUPOORT_TYPE;
}

static long roce3_ioctrl_reserved_2(struct roce3_device *rdev, void *buf)
{
    (void)(rdev);
    (void)(buf);
    return NOT_SUPOORT_TYPE;
}

static long roce3_ioctrl_reserved_3(struct roce3_device *rdev, void *buf)
{
    (void)(rdev);
    (void)(buf);
    return NOT_SUPOORT_TYPE;
}

static long roce3_ioctrl_reserved_4(struct roce3_device *rdev, void *buf)
{
    (void)(rdev);
    (void)(buf);
    return NOT_SUPOORT_TYPE;
}

static long roce3_ioctrl_reserved_5(struct roce3_device *rdev, void *buf)
{
    (void)(rdev);
    (void)(buf);
    return NOT_SUPOORT_TYPE;
}

static long roce3_ioctrl_reserved_6(struct roce3_device *rdev, void *buf)
{
    (void)(rdev);
    (void)(buf);
    return NOT_SUPOORT_TYPE;
}

